/**
 * Moondance UML modeling tool
 * 
 * This program is free software; you can redistribute it and
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package ro.pub.cs.se.moondance.editpart;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.gef.editpolicies.ConnectionEditPolicy;
import org.eclipse.gef.editpolicies.ConnectionEndpointEditPolicy;
import org.eclipse.gef.requests.DirectEditRequest;
import org.eclipse.gef.requests.GroupRequest;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.jface.viewers.TextCellEditor;

import ro.pub.cs.se.moondance.command.ConnectionDeleteCommand;
import ro.pub.cs.se.moondance.directedit.ConnectionMultiplicityCellEditorValidator;
import ro.pub.cs.se.moondance.directedit.ConnectionNameCellEditorValidator;
import ro.pub.cs.se.moondance.directedit.ExtendedDirectEditManager;
import ro.pub.cs.se.moondance.directedit.LabelCellEditorLocator;
import ro.pub.cs.se.moondance.directedit.ValidationMessageHandler;
import ro.pub.cs.se.moondance.editor.ValidationEnabledGraphicalViewer;
import ro.pub.cs.se.moondance.editor.figures.ConnectionFigure;
import ro.pub.cs.se.moondance.editor.figures.EditableLabel;
import ro.pub.cs.se.moondance.editpolicy.ConnectionDirectEditPolicy;
import ro.pub.cs.se.moondance.model.Connection;
import ro.pub.cs.se.moondance.model.ModelElement;

/**
 * Edit part for Connection model elements.
 */
public class ConnectionEditPart extends AbstractConnectionEditPart implements
		PropertyChangeListener {

	protected DirectEditManager manager[] = new DirectEditManager[3];
	public static final int NAME = 0;
	public static final int SOURCE_MULTIPLICITY = 1;
	public static final int TARGET_MULTIPLICITY = 2;

	private int editatedLabel = -1;

	/**
	 * Upon activation, attach to the model element as a property change
	 * listener.
	 */
	public void activate() {
		if (!isActive()) {
			super.activate();
			((ModelElement) getModel()).addPropertyChangeListener(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
	 */
	protected void createEditPolicies() {
		installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE,
				new ConnectionEndpointEditPolicy());
		installEditPolicy(EditPolicy.CONNECTION_ROLE,
				new ConnectionEditPolicy() {
					protected Command getDeleteCommand(GroupRequest request) {
						return new ConnectionDeleteCommand(getCastedModel());
					}
				});
		installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE,
				new ConnectionDirectEditPolicy());
	}

	// ******************* Direct editing related methods *********************/

	public int getEditatedLabel() {
		return editatedLabel;
	}

	/**
	 * @see org.eclipse.gef.EditPart#performRequest(org.eclipse.gef.Request)
	 */

	public void performRequest(Request request) {
		if (request.getType() == RequestConstants.REQ_DIRECT_EDIT) {
			if (request instanceof DirectEditRequest
					&& !directEditHitTest(((DirectEditRequest) request)
							.getLocation().getCopy()))
				return;
			performDirectEdit();
		}
	}

	private boolean directEditHitTest(Point requestLoc) {
		ConnectionFigure figure = (ConnectionFigure) getFigure();
		EditableLabel nameLabel = figure.getNameLabel();
		nameLabel.translateToRelative(requestLoc);
		if (nameLabel.containsPoint(requestLoc)) {
			editatedLabel = NAME;
			return true;
		}
		if (getCastedModel().getType().equals(Connection.AGGREGATION)
				|| getCastedModel().getType().equals(Connection.COMPOSITION)) {
			EditableLabel sourceMultiplicityLabel = figure
					.getSourceMultiplicityLabel();
			sourceMultiplicityLabel.translateToRelative(requestLoc);
			if (sourceMultiplicityLabel.containsPoint(requestLoc)) {
				editatedLabel = SOURCE_MULTIPLICITY;
				return true;
			}
			EditableLabel targetMultiplicityLabel = figure
					.getTargetMultiplicityLabel();
			targetMultiplicityLabel.translateToRelative(requestLoc);
			if (targetMultiplicityLabel.containsPoint(requestLoc)) {
				editatedLabel = TARGET_MULTIPLICITY;
				return true;
			}
		}
		return false;
	}

	protected void performDirectEdit() {
		if (manager[editatedLabel] == null) {
			ValidationEnabledGraphicalViewer viewer = (ValidationEnabledGraphicalViewer) getViewer();
			ValidationMessageHandler handler = viewer.getValidationHandler();

			ConnectionFigure figure = (ConnectionFigure) getFigure();
			switch (editatedLabel) {
			case NAME:
				EditableLabel nameLabel = figure.getNameLabel();
				manager[editatedLabel] = new ExtendedDirectEditManager(this,
						TextCellEditor.class, new LabelCellEditorLocator(
								nameLabel), nameLabel,
						new ConnectionNameCellEditorValidator(handler));
				break;

			case SOURCE_MULTIPLICITY:
				EditableLabel sourceLabel = figure.getSourceMultiplicityLabel();
				manager[editatedLabel] = new ExtendedDirectEditManager(this,
						TextCellEditor.class, new LabelCellEditorLocator(
								sourceLabel), sourceLabel,
						new ConnectionMultiplicityCellEditorValidator(handler));
				break;

			case TARGET_MULTIPLICITY:
				EditableLabel targetLabel = figure.getTargetMultiplicityLabel();
				manager[editatedLabel] = new ExtendedDirectEditManager(this,
						TextCellEditor.class, new LabelCellEditorLocator(
								targetLabel), targetLabel,
						new ConnectionMultiplicityCellEditorValidator(handler));
				break;
			}
		}
		manager[editatedLabel].show();
	}

	/**
	 * @param handles
	 *            the name change during an edit
	 */
	public void handleNameChange(String value) {
		ConnectionFigure connFigure = (ConnectionFigure) getFigure();
		EditableLabel label = connFigure.getNameLabel();
		label.setVisible(false);
		refreshVisuals();
	}

	/**
	 * Reverts to existing name in model when exiting from a direct edit
	 * (possibly before a commit which will result in a change in the label
	 * value)
	 */
	public void revertNameChange() {
		ConnectionFigure connFigure = (ConnectionFigure) getFigure();
		EditableLabel label = connFigure.getNameLabel();
		Connection conn = getCastedModel();
		label.setText(conn.getName());
		label.setVisible(true);
		refreshVisuals();
	}

	/**
	 * @param handles
	 *            the source multiplicity change during an edit
	 */
	public void handleSourceMultiplicityChange(String value) {
		ConnectionFigure connFigure = (ConnectionFigure) getFigure();
		EditableLabel label = connFigure.getSourceMultiplicityLabel();
		label.setVisible(false);
		refreshVisuals();
	}

	/**
	 * Reverts to existing source multiplicity in model when exiting from a
	 * direct edit (possibly before a commit which will result in a change in
	 * the label value)
	 */
	public void revertSourceMultiplicityChange() {
		ConnectionFigure connFigure = (ConnectionFigure) getFigure();
		EditableLabel label = connFigure.getSourceMultiplicityLabel();
		Connection conn = getCastedModel();
		label.setText(conn.getSourceMultiplicity());
		label.setVisible(true);
		refreshVisuals();
	}

	/**
	 * @param handles
	 *            the target multiplicity change during an edit
	 */
	public void handleTargetMultiplicityChange(String value) {
		ConnectionFigure connFigure = (ConnectionFigure) getFigure();
		EditableLabel label = connFigure.getTargetMultiplicityLabel();
		label.setVisible(false);
		refreshVisuals();
	}

	/**
	 * Reverts to existing target multiplicity in model when exiting from a
	 * direct edit (possibly before a commit which will result in a change in
	 * the label value)
	 */
	public void revertTargetMultiplicityChange() {
		ConnectionFigure connFigure = (ConnectionFigure) getFigure();
		EditableLabel label = connFigure.getTargetMultiplicityLabel();
		Connection conn = getCastedModel();
		label.setText(conn.getTargetMultiplicity());
		label.setVisible(true);
		refreshVisuals();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
	 */
	protected IFigure createFigure() {
		return new ConnectionFigure(getCastedModel().getType(),
				getCastedModel().getLineStyle());
	}

	/**
	 * Upon deactivation, detach from the model element as a property change
	 * listener.
	 */
	public void deactivate() {
		if (isActive()) {
			super.deactivate();
			((ModelElement) getModel()).removePropertyChangeListener(this);
		}
	}

	protected Connection getCastedModel() {
		return (Connection) getModel();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seejava.beans.PropertyChangeListener#propertyChange(java.beans.
	 * PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent event) {
		String property = event.getPropertyName();
		if (Connection.LINESTYLE_PROP.equals(property)) {
			((PolylineConnection) getFigure()).setLineStyle(getCastedModel()
					.getLineStyle());
		}
	}
}
